Infinite Impulse Response Filter


Tip
Despite the fact that FIR filters may have linear phase, they may require too many computations to be implemented, and are not appropriate for some applications when hardware is limited. IIR filters can be used in these cases as they require fewer computations than FIR filters. However, IIR filters usually do not have linear phase or it is very difficult to get a linear phase.
A pesar del hecho de que los filtros FIR tiene una fase lineal, estos requieren demasiadas operaciones para ser implementados en algunas aplicaciones donde el hardware es limitado. Los filtros IIR pueden ser usados en estos casos ya que estos requieres menos número de operaciones que los filtros FIR. Sin embargo, los filtros IIR usualmente no tienen una fase lineal o es muy difícil conseguir una fase lineal.

Butterworth low pass filter

The magnitude response of this type of filters is maximally flat in the pass band. For a continuous-time Butterworth low pass filter, magnitude-squared functions is as shown below. The poles of this type of filter that lie on the left part of the s-plane are shown in the same figure. In order to achieve a stable and causal filter, the poles on the left-part of the s-plane must be chosen.
La respuesta en amplitud de este tipo de filtros es plana al máximo en la banda de paso. Para un filtro pasa bajas de Butterworth en tiempo-continuo, el cuadrado de la función de amplitud es como se muestra debajo. Los polos de este tipo de filtros que caen en la parte izquierda del plano-s se muestran en la misma figura. A fin de conseguir un filtro causal y estable, los polos en la parte izquierda del plano-s deben de escogerse.

ButterworthMagnitude

Problem 1
Show that H(s) is transformed in H(z) as shown when the bilinear transformation is used.
Demostrar que H(s) se transforma en H(z) como se muestra cuando se usa la transformada bilineal.

BilinearTransformation

Problem 2
To transform a low-pass filter to a high-pass filter, the transformation shown below is used. Show that H(s) is transformed in H(z) as shown when this transformation is applied.
Para transformar un filtro pasa-bajas a un filtro pasa-altas, la transformación mostrada debajo es usada. Demostrar que H(s) se transforma en H(z) como se muestra cuando se aplica esta transformación.

BilinearTransformationHighPass

Problem 3
Create a Wintempla dialog application called Butterworth to design an IIR filter using a Butterworth approximation. Use Wintempla to create the GUI shown. The Math::IIRButterworth class uses a bi-quadratic structure for the filter implementation as shown in the system function below.
Cree una aplicación de dialogo de Wintempla llamada Butterworth para diseñar un filtro IIR usando una aproximación de Butterworth. Use Wintempla para crear la GUI mostrada. La clase Math::IIRButterworth usa una estructura bi-cuadrática para la implementación del filtro como se muestra en la función del sistema de abajo.

SystemFunction

ButterworthGUI

Butterworth.h
#pragma once //______________________________________ Butterworth.h
#include "Resource.h"
#define POINT_COUNT 512
#define FREQ_MAX 3.12
#define FREQ_RES 3120
#define GAIN_MIN -50.0
#define GAIN_RES 50

class Butterworth: public Win::Dialog
{
public:
     Butterworth()
     {
     }
     ~Butterworth()
     {
     }
     IIR::ButterworthFilter filter;
     void RefreshGraphs();
...
};


Butterworth.cpp
...

void Butterworth::Window_Open(Win::Event& e)
{
     this->Move(0, 0, true);
     //________________________________________________________ xyMagnitude
     xyMagnitude.CaptionX = L"Frequency (radians)";
     xyMagnitude.CaptionY = L"Gain (dB)";
     xyMagnitude.MinX= 0.0;
     xyMagnitude.MaxX= M_PI;
     xyMagnitude.MinY= -100.0;
     xyMagnitude.MaxY= 0.0;
     xyMagnitude.Graphs.Add(POINT_COUNT);
     const double deltaX = M_PI /(POINT_COUNT-1);
     for (int i=0; i<POINT_COUNT; i++)
     {
          xyMagnitude.Graphs[0][i].x = i*deltaX;
          xyMagnitude.Graphs[0][i].y = 0.0;
     }
     //________________________________________________________ xyGroupDelay
     xyGroupDelay.CaptionX = L"Frequency (radians)";
     xyGroupDelay.CaptionY = L"Group Delay (samples)";
     xyGroupDelay.MinX= 0.0;
     xyGroupDelay.MaxX= M_PI;
     xyGroupDelay.MinY= 0.0;
     xyGroupDelay.MaxY= 40.0;
     xyGroupDelay.Graphs.Add(POINT_COUNT);
     for (int i=0; i<POINT_COUNT; i++)
     {
          if (i == 0)
          {
               xyGroupDelay.Graphs[0][i].x = 0.000001;
          }
          else
          {
               xyGroupDelay.Graphs[0][i].x = i*deltaX;
          }
          xyGroupDelay.Graphs[0][i].y = 0.0;
     }

     //________________________________________________________ polarZeroPole
     polarZeroPole.Graphs.Add(); // Poles
     polarZeroPole.Graphs.Add(); // Zeros
     polarZeroPole.Graphs[0].Caption = L"Poles";
     polarZeroPole.Graphs[0].Type = Win::Graph::cross;
     //
     polarZeroPole.Graphs[1].Caption = L"Zeros";
     polarZeroPole.Graphs[1].Type = Win::Graph::circle;
     polarZeroPole.Graphs[1].Color = RGB(0, 180, 0);
     //________________________________________________________ sldCutFreq
     sldCutFreq.SetRange(1, FREQ_RES);
     sldCutFreq.Position = FREQ_RES/2;
     tbxCutFreq.DoubleValue = FREQ_MAX/2.0;
     //________________________________________________________ sldStopFreq
     sldStopFreq.SetRange(1, FREQ_RES);
     sldStopFreq.Position = FREQ_RES/2;
     tbxStopFreq.DoubleValue = FREQ_MAX/2.0;
     //________________________________________________________ sldStopGain
     sldStopGain.SetRange(1, GAIN_RES);
     sldStopGain.Position = GAIN_RES/2;
     tbxStopGain.DoubleValue = GAIN_MIN/2.0;
     //
     radioLowPass.Checked = true;
     RefreshGraphs();
}

void Butterworth::sldCutFreq_Hscroll(Win::Event& e)
{
     const int position = sldCutFreq.HasPositionChanged();
     if (position < 0) return;
     tbxCutFreq.DoubleValue = (position*FREQ_MAX)/FREQ_RES;
     RefreshGraphs();
}

void Butterworth::sldStopFreq_Hscroll(Win::Event& e)
{
     const int position = sldStopFreq.HasPositionChanged();
     if (position < 0) return;
     tbxStopFreq.DoubleValue = (position*FREQ_MAX)/FREQ_RES;
     RefreshGraphs();
}

void Butterworth::sldStopGain_Hscroll(Win::Event& e)
{
     const int position = sldStopGain.HasPositionChanged();
     if (position < 0) return;
     tbxStopGain.DoubleValue = (position*GAIN_MIN)/GAIN_RES;
     RefreshGraphs();
}

void Butterworth::radioLowPass_Click(Win::Event& e)
{
     RefreshGraphs();
}

void Butterworth::radioHighPass_Click(Win::Event& e)
{
     RefreshGraphs();
}

void Butterworth::RefreshGraphs()
{
     const double cutFreq = tbxCutFreq.DoubleValue;
     const double stopFreq = tbxStopFreq.DoubleValue;
     const double stopGain = tbxStopGain.DoubleValue;
     if (radioLowPass.Checked == true)
     {
          if (filter.CreateLowPass(cutFreq, -1.0, stopFreq, stopGain) == false)
          {
               tbxSystemFunction.Text = L"ERROR";
               return;
          }
     }
     else
     {
          if (filter.CreateHighPass(cutFreq, -1.0, stopFreq, stopGain) == false)
          {
               tbxSystemFunction.Text = L"ERROR";
               return;
          }
     }
     //
     double freq;
     double gain;
     int i;
     //___________________________________________________ Gain
     for (i=0; i<POINT_COUNT; i++)
     {
          freq = xyMagnitude.Graphs[0][i].x;
          gain = filter.biquadsCascade.GetMagnitude(freq);
          if (gain == 0)
          {
               xyMagnitude.Graphs[0][i].y = -200.0;
          }
          else
          {
               xyMagnitude.Graphs[0][i].y = 20.0*log10(gain);
          }
     }
     xyMagnitude.RefreshAll();
     //___________________________________________________ Group Delay
     for (i=0; i<POINT_COUNT; i++)
     {
          freq = xyGroupDelay.Graphs[0][i].x;
          xyGroupDelay.Graphs[0][i].y = filter.biquadsCascade.GetGroupDelay(freq);
     }
     xyGroupDelay.AutoScaleY(false);
     //___________________________________________________ Poles
     double real1, imag1, real2, imag2;
     int j = 0;
     const int size = filter.biquadsCascade.GetSize();
     polarZeroPole.Graphs[0].SetPointCount(size*2);
     for (i = 0; i < size; i++)
     {
          filter.biquadsCascade[i].GetPoles(real1, imag1, real2, imag2);
          polarZeroPole.Graphs[0][j].x = atan2(imag1, real1);
          polarZeroPole.Graphs[0][j].y = sqrt(real1*real1 + imag1*imag1);
          j++;
          polarZeroPole.Graphs[0][j].x = atan2(imag2, real2);
          polarZeroPole.Graphs[0][j].y = sqrt(real2*real2 + imag2*imag2);
          j++;
     }
     //___________________________________________________ Zeros
     j = 0;
     polarZeroPole.Graphs[1].SetPointCount(size*2);
     for (i = 0; i < size; i++)
     {
          filter.biquadsCascade[i].GetZeros(real1, imag1, real2, imag2);
          polarZeroPole.Graphs[1][j].x = atan2(imag1, real1);
          polarZeroPole.Graphs[1][j].y = sqrt(real1*real1 + imag1*imag1);
          j++;
          polarZeroPole.Graphs[1][j].x = atan2(imag2, real2);
          polarZeroPole.Graphs[1][j].y = sqrt(real2*real2 + imag2*imag2);
          j++;
     }
     polarZeroPole.Refresh();
     //___________________________________________________ System Function
     IIR::BiquadsCascade biquadsCascade = filter.biquadsCascade;
     const int N = biquadsCascade.GetSize();
     wstring text;
     wchar_t wtext[512];
     for (int i = 0; i < N; i++)
     {
          _snwprintf_s(wtext, 512, _TRUNCATE, L"b0 = %.10f\r\nb1 = %.10f\r\nb2 = %.10f\r\na1 = %.10f\r\na2 = %.10f\r\n__________________\r\n",
               biquadsCascade[i].b0, biquadsCascade[i].b1, biquadsCascade[i].b2, biquadsCascade[i].a1, biquadsCascade[i].a2);
          text += wtext;
     }
     tbxSystemFunction.Text = text;
}


ButterworthRun1

ButterworthRun2

ButterworthRun3

Problem 4
Create a Wintempla dialog application called Chebyshev to design an IIR filter using a Chebyshev approximation. Use Wintempla to create the GUI shown. The code of this problem is very similar to the code of the previous problem; the main difference is use the Math::IIRChebyshev class instead of the Math::IIRButterworth class.
Cree una aplicación de dialogo de Wintempla llamada Chebyshev para diseñar un filtro IIR usando una aproximación de Chebyshev . Use Wintempla para crear la GUI mostrada. El código de este problema es muy semejante al código del problema previo; la diferencia principal es usar la clase Math::IIRChebyshev en lugar de la clase Math::IIRButterworth.

Chebyshev

ChebyshevGUI

Chebyshev.h
#pragma once //______________________________________ Chebyshev.h
#include "Resource.h"
#define POINT_COUNT 512
#define FREQ_MAX 3.12
#define FREQ_RES 3120
#define GAIN_MIN -50.0
#define GAIN_RES 50

class Chebyshev: public Win::Dialog
{
public:
     Chebyshev()
     {
     }
     ~Chebyshev()
     {
     }
     IIR::ChebyshevFilter filter;
     void RefreshGraphs();
     ...
};


Chebyshev.cpp
...
void Chebyshev::Window_Open(Win::Event& e)
{
     this->Move(0, 0, true);
     //________________________________________________________ xyMagnitude
     xyMagnitude.CaptionX = L"Frequency (radians)";
     xyMagnitude.CaptionY = L"Gain (dB)";
     xyMagnitude.MinX= 0.0;
     xyMagnitude.MaxX= M_PI;
     xyMagnitude.MinY= -90.0;
     xyMagnitude.MaxY= 10.0;
     xyMagnitude.Graphs.Add(POINT_COUNT);
     const double deltaX = M_PI /(POINT_COUNT-1);
     for(int i=0; i<POINT_COUNT; i++)
     {
          xyMagnitude.Graphs[0][i].x = i*deltaX;
          xyMagnitude.Graphs[0][i].y = 0.0;
     }
     //________________________________________________________ xyGroupDelay
     xyGroupDelay.CaptionX = L"Frequency (radians)";
     xyGroupDelay.CaptionY = L"Group Delay (samples)";
     xyGroupDelay.MinX= 0.0;
     xyGroupDelay.MaxX= M_PI;
     xyGroupDelay.MinY= 0.0;
     xyGroupDelay.MaxY= 40.0;
     xyGroupDelay.Graphs.Add(POINT_COUNT);
     for(int i=0; i<POINT_COUNT; i++)
     {
          if (i == 0)
          {
               xyGroupDelay.Graphs[0][i].x = 0.000001;
          }
          else
          {
               xyGroupDelay.Graphs[0][i].x = i*deltaX;
          }
          xyGroupDelay.Graphs[0][i].y = 0.0;
     }
     //________________________________________________________ polarZeroPole
     polarZeroPole.Graphs.Add(); // Poles
     polarZeroPole.Graphs.Add(); // Zeros
     polarZeroPole.Graphs[0].Caption = L"Poles";
     polarZeroPole.Graphs[0].Type = Win::Graph::cross;
     //
     polarZeroPole.Graphs[1].Caption = L"Zeros";
     polarZeroPole.Graphs[1].Type = Win::Graph::circle;
     polarZeroPole.Graphs[1].Color = RGB(0, 180, 0);
     //________________________________________________________ sldCutFreq
     sldCutFreq.SetRange(1, FREQ_RES);
     sldCutFreq.Position = FREQ_RES/2;
     tbxCutFreq.DoubleValue = FREQ_MAX/2.0;
     //________________________________________________________ sldStopFreq
     sldStopFreq.SetRange(1, FREQ_RES);
     sldStopFreq.Position = FREQ_RES/2;
     tbxStopFreq.DoubleValue = FREQ_MAX/2.0;
     //________________________________________________________ sldStopGain
     sldStopGain.SetRange(1, GAIN_RES);
     sldStopGain.Position = GAIN_RES/2;
     tbxStopGain.DoubleValue = GAIN_MIN/2.0;
     //
     radioLowPass.Checked = true;
     RefreshGraphs();
}

void Chebyshev::sldCutFreq_Hscroll(Win::Event& e)
{
     const int position = sldCutFreq.HasPositionChanged();
     if (position < 0) return;
     tbxCutFreq.DoubleValue = (position*FREQ_MAX)/FREQ_RES;
     RefreshGraphs();
}

void Chebyshev::sldStopFreq_Hscroll(Win::Event& e)
{
     const int position = sldStopFreq.HasPositionChanged();
     if (position < 0) return;
     tbxStopFreq.DoubleValue = (position*FREQ_MAX)/FREQ_RES;
     RefreshGraphs();
}

void Chebyshev::sldStopGain_Hscroll(Win::Event& e)
{
     const int position = sldStopGain.HasPositionChanged();
     if (position < 0) return;
     tbxStopGain.DoubleValue = (position*GAIN_MIN)/GAIN_RES;
     RefreshGraphs();
}

void Chebyshev::radioLowPass_Click(Win::Event& e)
{
     RefreshGraphs();
}

void Chebyshev::radioHighPass_Click(Win::Event& e)
{
     RefreshGraphs();
}

void Chebyshev::RefreshGraphs()
{
     const double cutFreq = tbxCutFreq.DoubleValue; //0.1*M_PI;
     const double stopFreq = tbxStopFreq.DoubleValue; //0.19530*M_PI;
     const double stopGain = tbxStopGain.DoubleValue; //-20.0;
     if (radioLowPass.Checked == true)
     {
          if (filter.CreateLowPass(cutFreq, 1.0, stopFreq, stopGain) == false)
          {
               tbxSystemFunction.Text = L"ERROR";
               return;
          }
     }
     else
     {
          if (filter.CreateHighPass(cutFreq, 1.0, stopFreq, stopGain) == false)
          {
               tbxSystemFunction.Text = L"ERROR";
               return;
          }
     }
     //
     double freq;
     double gain;
     int i;
     //___________________________________________________ Gain
     for(i=0; i<POINT_COUNT; i++)
     {
          freq = xyMagnitude.Graphs[0][i].x;
          gain = filter.biquadsCascade.GetMagnitude(freq);
          if (gain == 0)
          {
               xyMagnitude.Graphs[0][i].y = -200.0;
          }
          else
          {
               xyMagnitude.Graphs[0][i].y = 20.0*log10(gain);
          }
     }
     xyMagnitude.RefreshAll();
     //___________________________________________________ Group Delay
     for(i=0; i<POINT_COUNT; i++)
     {
          freq = xyGroupDelay.Graphs[0][i].x;
          xyGroupDelay.Graphs[0][i].y = filter.biquadsCascade.GetGroupDelay(freq);
     }
     xyGroupDelay.AutoScaleY(false);
     //___________________________________________________ Poles
     double real1, imag1, real2, imag2;
     int j = 0;
     const int size = filter.biquadsCascade.GetSize();
     polarZeroPole.Graphs[0].SetPointCount(size*2);
     for (i = 0; i < size; i++)
     {
          filter.biquadsCascade[i].GetPoles(real1, imag1, real2, imag2);
          polarZeroPole.Graphs[0][j].x = atan2(imag1, real1);
          polarZeroPole.Graphs[0][j].y = sqrt(real1*real1 + imag1*imag1);
          j++;
          polarZeroPole.Graphs[0][j].x = atan2(imag2, real2);
          polarZeroPole.Graphs[0][j].y = sqrt(real2*real2 + imag2*imag2);
          j++;
     }
     //___________________________________________________ Zeros
     j = 0;
     polarZeroPole.Graphs[1].SetPointCount(size*2);
     for (i = 0; i < size; i++)
     {
          filter.biquadsCascade[i].GetZeros(real1, imag1, real2, imag2);
          polarZeroPole.Graphs[1][j].x = atan2(imag1, real1);
          polarZeroPole.Graphs[1][j].y = sqrt(real1*real1 + imag1*imag1);
          j++;
          polarZeroPole.Graphs[1][j].x = atan2(imag2, real2);
          polarZeroPole.Graphs[1][j].y = sqrt(real2*real2 + imag2*imag2);
          j++;
     }
     polarZeroPole.Refresh();
     //___________________________________________________ System Function
     IIR::BiquadsCascade biquadsCascade = filter.biquadsCascade;
     const int N = biquadsCascade.GetSize();
     wstring text;
     wchar_t wtext[512];
     for (int i = 0; i < N; i++)
     {
          _snwprintf_s(wtext, 512, _TRUNCATE, L"b0 = %.10f\r\nb1 = %.10f\r\nb2 = %.10f\r\na1 = %.10f\r\na2 = %.10f\r\n__________________\r\n",
               biquadsCascade[i].b0, biquadsCascade[i].b1, biquadsCascade[i].b2, biquadsCascade[i].a1, biquadsCascade[i].a2);
          text += wtext;
     }
     tbxSystemFunction.Text = text;
}


ChebyshevRun

Problem 5
Create a Wintempla dialog application called Elliptic to design an IIR filter using an Elliptic approximation. Use the IIR::EllipticFilter class. Remember that this class is designed for a bi-quadratic structure implementation and the order of the filter will always be even.
Cree una aplicación de dialogo de Wintempla llamada Elliptic para diseñar un filtro IIR usando una aproximación Eliptica. Use la clase IIR::EllipticFilter. Recuerde que esta clase está diseñada para implementarse en una estructura bi-cuadrática y el orden del filtro será siempre par.

EllipticRun

Problem 6
Create a Wintempla dialog application called LinkPlay to play a wave file using two Linkwitz-Riley filters.
Cree una aplicación de diálogo de Wintempla llamada LinkPlay para reproducir un archivo wave usando dos filtros Linkwitz-Riley.

Step A
Edit the stdafx.h file to activate the DAC and ADC by removing the comments of the shown line.
Edite el archivo stdafx.h para activar el DAC y el ADC removiendo los comentarios de la línea mostrada.

stdafx.h
...
//_________________________________________ MIDI, Audio Card DAC's and ADC's (or GDI Game for timers)
//#define WIN_DAC_ADC_SUPPORT
...


Step B
Use Wintempla to insert a Digital to Analog Converter (DAC). Once Wintempla is open, click the Show All Controls in Toolbox to show all controls. Set the name to dacOutput. In the Events tab, be sure all events are unselected. Insert a Signal View (be sure all events are unselected). Insert a button to Play and another one to Stop as shown. Insert a Drop Down List to select the output device. Then, insert a slider (with the Hscroll event), a textbox and a label) for the frequency. Finally, insert two labels and two sliders (with the Hscroll event) for the low frequency gain and the high frequency gain as shown.
Use Wintempla para inserte un Digital to Analog Converter (DAC). Una vez que Wintempla se abre, haga clic en Show All Controls in Toolbox para mostrar todos los controles. Fije el nombre a dacOutput. En la pestaña de eventos, asegúrese que todos los eventos están deseleccionados. Inserte un Signal View (asegúrese que todos los eventos están deseleccionados). Inserte un botón para Reproducir y otro para Detener como se muestra. Inserte una Drop Down List para seleccionar el dispositivo de salida. Entonces, inserte un slider (con el evento Hscroll), una caja de texto y una etiqueta. Finalmente, inserte dos etiquetas y dos sliders (con el evento Hscroll) para la ganancia de la baja frecuencia y para la ganancia para la frecuencia alta como se muestra.

LinkPlayGui

SliderEvent

DacEvent

Step C
Edit the LinkPlay.h file and the LinkPlay.cpp file to implement the three functions of the Mm::IAudioOut interface (Observe the the LinkPlay class is derived from Mm::IAudioOut). Remember that an interface is used to pass a set of functions to another function or another object.
Edite los archivos LinkPlay.h y LinkPlay.cpp para implementar las tres funciones de la interface Mm::IAudioOut (Observa que la clase LinkPlay se deriva de Mm::IAudioOut). Recuerde que una interface es usada para pasar un conjunto de funciones a otra función u objeto.

LinkPlay.h
#pragma once //______________________________________ LinkPlay.h
#include "Resource.h"
#define FREQ_RES 100
#define FREQ_MIN 80.0
#define FREQ_MAX 14000.0
class LinkPlay: public Win::Dialog, public Mm::IAudioOut
{
public:
     LinkPlay()
     {
          samplesPerSec = 0;
          highGain = 1.0;
          lowGain = 1.0;
     }
     ~LinkPlay()
     {
     }
     double highGain;
     double lowGain;
     unsigned int samplesPerSec = 0;
     void CreateFilters();
     IIR::BiquadSection lowRightFilter;
     IIR::BiquadSection lowLeftFilter;
     IIR::BiquadSection highLeftFilter;
     IIR::BiquadSection highRightFilter;
     Mm::WaveFile waveFile;
     //______________________________________________________________ Mm::IAudioOut
     void AudioOutStarted(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution);
     void AudioOutData(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution, WAVEHDR* waveHdr);
     void AudioOutStopped();
protected:
     ...
};


PlayLink.cpp
...
void LinkPlay::Window_Open(Win::Event& e)
{
     btStop.Enabled = false;
     //________________________________________________________ ddDevice
     const int count = ::waveOutGetNumDevs();
     WAVEOUTCAPS woc;
     const int wsize= sizeof(WAVEOUTCAPS);
     for (int i = 0; i < count; i++)
     {
          if (::waveOutGetDevCaps(i, &woc, wsize) == MMSYSERR_NOERROR)
          {
               ddDevice.Items.Add(woc.szPname, i);
          }
     }
     ddDevice.SelectedIndex = 0;
     //________________________________________________________ sldCutFrequency
     sldCutFrequency.SetRange(0, FREQ_RES);
     sldCutFrequency.Position = FREQ_RES/2;
     tbxCutFrequency.DoubleValue = FREQ_MIN+ 0.5*(FREQ_MAX - FREQ_MIN);
     //________________________________________________________ sldHighFreqGain
     sldHighFreqGain.SetRange(-50, 0);
     sldHighFreqGain.Position = 0;
     //________________________________________________________ sldLowFreqGain
     sldLowFreqGain.SetRange(-50, 0);
     sldLowFreqGain.Position = 0;
}

void LinkPlay::CreateFilters()
{
     const double cutFreq_Hertz = tbxCutFrequency.DoubleValue;
     lowRightFilter.CreateLinkwitzRileyLowPass(cutFreq_Hertz, samplesPerSec);
     lowLeftFilter.CreateLinkwitzRileyLowPass(cutFreq_Hertz, samplesPerSec);
     highLeftFilter.CreateLinkwitzRileyHighPass(cutFreq_Hertz, samplesPerSec);
     highRightFilter.CreateLinkwitzRileyHighPass(cutFreq_Hertz, samplesPerSec);
}

void LinkPlay::sldCutFrequency_Hscroll(Win::Event& e)
{
     const int position = sldCutFrequency.HasPositionChanged();
     if (position < 0) return;
     tbxCutFrequency.DoubleValue = FREQ_MIN+ position*(FREQ_MAX - FREQ_MIN)/FREQ_RES;
     CreateFilters();
}

void LinkPlay::btPlay_Click(Win::Event& e)
{
     //________________________________________________________ 1. Prompt to the user to get the filename
     Win::FileDlg dlg;
     dlg.Clear();
     dlg.SetFilter(L"Wave files (*.wav)\0*.wav\0\0", 0, L"wav");
     if (dlg.BeginDialog(hWnd, L"Open", false) != TRUE) return;
     //________________________________________________________ 2. Get the Device ID
     LPARAM deviceID = WAVE_MAPPER;
     ddDevice.GetSelectedData(deviceID);
     //________________________________________________________ 3. Open the Wave File
     const wchar_t* error = waveFile.OpenForReading(dlg.GetFileNameFullPath());
     if (error != NULL)
     {
          this->MessageBox(error, L"FilePlayer", MB_OK | MB_ICONERROR);
          return;
     }
     //________________________________________________________ 4. Start the DAC
     error = dacOutput.Start((unsigned int)deviceID, waveFile.GetSamplesPerSecond(), waveFile.GetNumChannels(), waveFile.GetBitsResolution(), 16384, this);
     if (error != NULL)
     {
          this->MessageBox(error, L"FilePlayer", MB_OK | MB_ICONERROR);
          return;
     }
}

void LinkPlay::btStop_Click(Win::Event& e)
{
     dacOutput.Stop();
}

void LinkPlay::AudioOutStarted(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution)
{
     this->samplesPerSec = samplesPerSec;
     btPlay.Enabled = false;
     btStop.Enabled = true;
     ddDevice.Enabled = false;
     EnableCloseButton(false);
     CreateFilters();
}

void LinkPlay::AudioOutData(unsigned int samplesPerSec, unsigned int numbChannels, unsigned int bitsResolution, WAVEHDR* waveHdr)
{
     waveHdr->dwBytesRecorded = waveFile.ReadData(waveHdr->lpData, waveHdr->dwBufferLength);
     Sys::Sample16 *samples = (Sys::Sample16 *)waveHdr->lpData;
     const int numSamples = (waveHdr->dwBytesRecorded)/4; // four bytes per sample
     double left, right;
     double low, high;
     int i;
     for (i = 0; i < numSamples; i++)
     {
          //________________________________________________________________ LEFT
          low = lowGain*lowLeftFilter.ComputeOutput(samples[i].channel_1);
          high = highGain*highLeftFilter.ComputeOutput(samples[i].channel_1);
          left = (low-high)/2.0;
          if (left > 32765.0) left = 32765.0;
          if (left < -32765.0) left = -32765.0;
          samples[i].channel_1 = (__int16)(left);
          //________________________________________________________________ RIGHT
          low = lowGain*lowRightFilter.ComputeOutput(samples[i].channel_2);
          high = highGain*highRightFilter.ComputeOutput(samples[i].channel_2);
          right = (low-high)/2.0;
          if (right > 32765.0) right = 32765.0;
          if (right < -32765.0) right = -32765.0;
          samples[i].channel_2 = (__int16)(right);
     }
     svMain.RefreshFromDAC(waveHdr, numbChannels, bitsResolution);
}

void LinkPlay::AudioOutStopped()
{
     btPlay.Enabled = true;
     btStop.Enabled = false;
     ddDevice.Enabled = true;
     waveFile.Close();
     EnableCloseButton(true);
}

void LinkPlay::sldHighFreqGain_Hscroll(Win::Event& e)
{
     const int position = sldHighFreqGain.Position;
     highGain = pow(10.0, position/20.0);
}

void LinkPlay::sldLowFreqGain_Hscroll(Win::Event& e)
{
     const int position = sldLowFreqGain.Position;
     lowGain = pow(10.0, position/20.0);
}


LinkPlayRun

Problem 7
Create a Wintempla dialog application called LowPassBoost to design an IIR Shelving filter to boost bass.
Cree una aplicación de dialogo de Wintempla llamada LowPassBoost para diseñar un filtro IIR Shelving para amplificar los bajos.

LowPassBoost.h
#pragma once //______________________________________ LowPassBoost.h
#include "Resource.h"
#define POINT_COUNT 512
class LowPassBoost: public Win::Window
{
public:
     LowPassBoost()
     {
     }
     ~LowPassBoost()
     {
     }
     void RefreshGraph();
     const wchar_t * GetClassName(){return L"LowPassBoost";}
     . . .
};


LowPassBoost.cpp
. . .
void LowPassBoost::Window_Open(Win::Event& e)
{
     //________________________________________________________ xyMagnitude
     xyMagnitude.CaptionX = L"Frequency (Hz)";
     xyMagnitude.CaptionY = L"Gain (dB)";
     xyMagnitude.MinX= 0.0;
     xyMagnitude.MaxX= 1000.0;
     xyMagnitude.MinY= -25.0;
     xyMagnitude.MaxY= 5.0;
     xyMagnitude.DivisionCountY = 6;
     xyMagnitude.Graphs.Add(POINT_COUNT);
     const double deltaX = xyMagnitude.MaxX /(POINT_COUNT-1);
     for (int i=0; i<POINT_COUNT; i++)
     {
          xyMagnitude.Graphs[0][i].x = i*deltaX;
          xyMagnitude.Graphs[0][i].y = 0.0;
     }
     //________________________________________________________ sldCutFreq
     sldCutFreq.SetRange(30, 200);
     sldCutFreq.Position = 100;
     tbxCutFreq.DoubleValue = sldCutFreq.Position;
     //________________________________________________________ sldGain
     sldGain.SetRange(1, 20);
     sldGain.Position = 6;
     tbxGain.DoubleValue = sldGain.Position;
     //________________________________________________________ polarZeroPole
     polarZeroPole.Graphs.Add(); // Poles
     polarZeroPole.Graphs.Add(); // Zeros
     polarZeroPole.Graphs[0].Caption = L"Poles";
     polarZeroPole.Graphs[0].Type = Win::Graph::cross;
     //
     polarZeroPole.Graphs[1].Caption = L"Zeros";
     polarZeroPole.Graphs[1].Type = Win::Graph::circle;
     polarZeroPole.Graphs[1].Color = RGB(0, 180, 0);
     //________________________________________________________ sldQ
     sldQ.SetRange(100, 200);
     sldQ.Position = 100;
     tbxQ.DoubleValue = 100.0/200.0;
     //
     RefreshGraph();
}

void LowPassBoost::sldCutFreq_Hscroll(Win::Event& e)
{
     int position = 0;
     if (sldCutFreq.GetPosition(position) == false) return;
     tbxCutFreq.DoubleValue = position;
     RefreshGraph();
}

void LowPassBoost::sldGain_Hscroll(Win::Event& e)
{
     int position = 0;
     if (sldGain.GetPosition(position) == false) return;
     tbxGain.DoubleValue = position;
     RefreshGraph();
}

void LowPassBoost::sldQ_Hscroll(Win::Event& e)
{
     int position = 0;
     if (sldQ.GetPosition(position) == false) return;
     tbxQ.DoubleValue = position/200.0;
     RefreshGraph();
}

void LowPassBoost::RefreshGraph()
{
     const double samplingFreq = 44100.0;
     const double fc = tbxCutFreq.DoubleValue;
     const double gain = tbxGain.DoubleValue;
     const double Q = tbxQ.DoubleValue;
     IIR::BiquadSection biquad;
     biquad.CreateShelvingLow(fc, samplingFreq, gain, Q, true);
     //biquad.CreateShelvingHigh(fc, samplingFreq, gain, Q, true);
     IIR::PolarBiquadSection polarBiquad = biquad;
     double freqHz;
     double freqRad;
     double g;
     int i;
     //___________________________________________________ 1. Magnitude
     for (i = 0; i < POINT_COUNT; i++)
     {
          freqHz= xyMagnitude.Graphs[0][i].x;
          freqRad = 2.0*M_PI*freqHz/samplingFreq;
          g = polarBiquad.GetMagnitude(freqRad);
          if (g == 0.0)
          {
               xyMagnitude.Graphs[0][i].y = -200.0;
          }
          else
          {
               xyMagnitude.Graphs[0][i].y = 20.0*log10(g);
          }
     }
     xyMagnitude.RefreshAll();
     //___________________________________________________ 2. Poles
     double real1, imag1, real2, imag2;
     polarZeroPole.Graphs[0].SetPointCount(2);
     polarBiquad.GetPoles(real1, imag1, real2, imag2);
     // Pole 1
     polarZeroPole.Graphs[0][0].x = atan2(imag1, real1);
     polarZeroPole.Graphs[0][0].y = sqrt(real1*real1 + imag1*imag1);
     // Pole 2
     polarZeroPole.Graphs[0][1].x = atan2(imag2, real2);
     polarZeroPole.Graphs[0][1].y = sqrt(real2*real2 + imag2*imag2);
     //___________________________________________________ 3. Zeros
     polarZeroPole.Graphs[1].SetPointCount(2);
     polarBiquad.GetZeros(real1, imag1, real2, imag2);
     // Zero 1
     polarZeroPole.Graphs[1][0].x = atan2(imag1, real1);
     polarZeroPole.Graphs[1][0].y = sqrt(real1*real1 + imag1*imag1);
     // Zero 2
     polarZeroPole.Graphs[1][1].x = atan2(imag2, real2);
     polarZeroPole.Graphs[1][1].y = sqrt(real2*real2 + imag2*imag2);
     //
     polarZeroPole.Refresh();
}


LowPassBoostRun

© Copyright 2000-2021 Wintempla selo. All Rights Reserved. Jul 22 2021. Home